#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <fcntl.h>

ssize_t read_fd(int fd, void* ptr, size_t nbytes, int* recvfd) {
	struct msghdr msg;
	struct iovec iov[1];
	ssize_t n;

	int newfd;
	union {
		struct cmsghdr cm;
		char control[CMSG_SPACE(sizeof(int))];
	} control_un;
	struct cmsghdr *cmptr;

	msg.msg_control = control_un.control;
	msg.msg_controllen = sizeof(control_un.control);
	msg.msg_name = NULL;
	msg.msg_namelen = 0;

	iov[0].iov_base = ptr;
	iov[0].iov_len = nbytes;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	if ((n=recvmsg(fd, &msg, 0)) <= 0) {
		return n;
	}

	if ((cmptr=CMSG_FIRSTHDR(&msg)) != NULL && \
		cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
		if (cmptr->cmsg_level != SOL_SOCKET) {
			printf("control level != SOL_SOCKET\n");
			return -1;
		}

		if (cmptr->cmsg_type != SCM_RIGHTS) {
			printf("control type != SCM_RIGHTS\n");
			return -1;
		}

		*recvfd = *((int*)CMSG_DATA(cmptr));
	}
	else {
		*recvfd = -1;
	}
}

int my_open(char* pathname, int mode) {
	int fd, sockfd[2], status;
	pid_t childpid;
	char c, argsockfd[10], argmode[10];

	socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);

	if ((childpid=fork()) == 0) {
		close(sockfd[0]);
		snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]);
		snprintf(argmode, sizeof(argmode), "%d", mode);
		execl("./openfile", "openfile", argsockfd, pathname, argmode, (char*)NULL);
		printf("execl error\n");
		exit(-1);
	}

	close(sockfd[1]);
	waitpid(childpid, &status, 0);
	if (WIFEXITED(status) == 0) {
		printf("child not exit\n");
		return -1;
	}
	if ((status = WEXITSTATUS(status)) == 0) {
		read_fd(sockfd[0], &c, 1, &fd);
	}
	else {
		errno = status;
		fd = -1;
	}

	close(sockfd[0]);
	return fd;
}

int main(int argc, char *argv[])
{
	int fd, n;
	char buff[1024];

	if (argc != 2) {
		printf("usage: mycat args\n");
		return -1;
	}

	if ((fd=my_open(argv[1], O_RDONLY)) < 0) {
		printf("mycat open error: %s\n", strerror(errno));
		return -1;
	}

	while ((n=read(fd, buff, 1024)) > 0) 
		write(STDOUT_FILENO, buff, n);
	
	return 0;
}